package com.blog.cloud.security.provider;

import com.blog.cloud.core.exception.BlogException;
import com.blog.cloud.security.service.UserDetailService;
import com.blog.cloud.sms.service.SmsService;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;

import java.util.Objects;

@Getter
@Setter
@RequiredArgsConstructor
public class MobileAuthenticationProvider implements AuthenticationProvider {

    private final UserDetailService userDetailsService;

    private final SmsService tencentSmsService;

    /**
     * 认证
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 判断认证对象类型，只支持MobileAuthenticationToken认证对象
        Assert.isInstanceOf(MobileAuthenticationToken.class, authentication, "Only MobileAuthenticationToken is supported");
        // 从认证对象中获取手机号
        String mobile = (String) authentication.getPrincipal();
        String code = (String) authentication.getCredentials();

        // 根据手机号查询用户
        UserDetails userDetails = userDetailsService.loadUserByMobile(mobile);
        if (Objects.isNull(userDetails)) {
            throw new BlogException("未查到该手机号用户");
        }

        // 校验短信验证码
        Boolean result = tencentSmsService.validate(mobile, code);
        if (!result) {
            throw new BlogException("短信验证码已过期");
        }
        // 构建认证成功的认证对象
        return createSuccessAuthentication(userDetails);

    }

    private Authentication createSuccessAuthentication(UserDetails userDetails) {
        // 返回认证对象时将用户详细信息也一并封装进去，用于返回给前端
        Authentication authentication = new MobileAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        authentication.setAuthenticated(true);
        return authentication;
    }

    /**
     * 判断是否支持指定类型的认证对象，只支持MobileAuthenticationToken认证
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return (MobileAuthenticationToken.class.isAssignableFrom(authentication));
    }
}
